import React from "react";
import {
  Callbacks,
  FormInstance,
  InternalNamePath,
  NamePath,
  Store,
  StoreValue,
} from "./interface";

// 自定义hooks 获取instance实例
export default function useForm<Values = any>(form?: FormInstance<Values>) {
  const formRef = React.useRef<FormInstance>();
  // forceUpdate 用来刷新组件
  const [, forceUpdate] = React.useState({});

  if (!formRef.current) {
    // 没有才创建
    if (form) formRef.current = form; // 可以传入form的
    const forceReRender = () => {
      forceUpdate({});
    };
    const formStore: FormStore = new FormStore(forceReRender);
    formRef.current = formStore.getForm();
  }
  return [formRef.current];
}

export class FormStore {
  private forceRootUpdate: () => void;
  private callbacks: Callbacks = {};
  // store的值 表单的值都保存在这里面
  private store: Store = {};
  constructor(forceRootUpdate: () => void) {
    this.forceRootUpdate = forceRootUpdate;
  }
  // 为什么不直接 return this 内部有一些变量和方法不希望外部可以访问到
  public getForm() {
    return {
      getFieldValue: this.getFieldValue,
      setFieldValue: this.setFieldValue,
      submit: this.submit,
      setCallbacks: this.setCallbacks,
    };
  }

  private getFieldValue = (name: NamePath) => {
    return this.getValue(this.store, name);
  };

  private setFieldValue = (name: NamePath, value: any) => {
    // this.setFields([
    //   {
    //     name,
    //     value,
    //   },
    // ]);
    this.setValue(this.store, name, value);
  };

  // 可以设置多个
  // private setFields(fields: FieldData[]) {}

  // 可以是多个的
  getValue(store: Store, namePath: any) {
    // https://github.com/react-component/util/blob/master/src/utils/get.ts
    // const value = get(store, namePath);
    // return value;
    return store[namePath];
  }

  setValue(store: Store, name: any, value: StoreValue) {
    // const newStore = set(store, namePath, value);
    // return newStore;
    store[name] = value;
  }

  private submit = () => {
    const { onFinish } = this.callbacks;
    // validateFields
    if (onFinish) {
      onFinish(this.store);
    }
  };

  private setCallbacks = (callbacks: Callbacks) => {
    this.callbacks = callbacks;
  };
}

export function get(
  entity: any,
  path: (string | number)[] | readonly (string | number)[]
) {
  let current = entity;

  for (let i = 0; i < path.length; i += 1) {
    if (current === null || current === undefined) {
      return undefined;
    }

    current = current[path[i]];
  }

  return current;
}
